package com.anivia.common.log.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.anivia.common.core.model.vo.R;
import com.anivia.common.log.AniviaRequestWrapper;
import com.anivia.common.log.AniviaResponseWrapper;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;

@Slf4j
public class HttpWrapperUtil {

    /**
     * 封装请求处理
     *
     * @param request         请求
     * @param response        响应
     * @param requestWrapper  请求封装类型
     * @param responseWrapper 响应封装类型
     */
    public static void destroy(HttpServletRequest request,
                               HttpServletResponse response,
                               HttpServletRequest requestWrapper,
                               AniviaResponseWrapper responseWrapper,
                               byte[] responseBody) throws IOException {
        if (!(request instanceof AniviaRequestWrapper) && requestWrapper != null) {
            if (requestWrapper instanceof AniviaRequestWrapper) {
                ((AniviaRequestWrapper) requestWrapper).destroy();
            }
        }
        if (!(response instanceof AniviaResponseWrapper) && responseWrapper != null) {
            try {
                if (responseBody != null && responseBody.length != 0) {
                    responseWrapper.responseOutWrite(responseBody);
                }
            } finally {
                responseWrapper.destroy();
            }
        }
    }

    /**
     * 判断是否可以获取body值（只有在application/json和application/xml可以获取body）
     *
     * @param request 请求
     * @return {@code true} 可以获取body {@code false} 不能获取
     */
    public static boolean isGetBody(HttpServletRequest request) {
        final String contentType = request.getContentType();
        if (StrUtil.isEmpty(contentType)) {
            return false;
        }
        return contentType.startsWith("application/json") || contentType.startsWith("application/xml");
    }

    /**
     * 判断是否可以获取body值（只有在application/json和application/xml可以获取body）
     *
     * @param response 响应
     * @return {@code true} 可以获取body {@code false} 不能获取
     */
    public static boolean isGetBody(HttpServletResponse response) {
        final String contentType = response.getContentType();
        if (StrUtil.isEmpty(contentType)) {
            return false;
        }
        return contentType.startsWith("application/json") || contentType.startsWith("application/xml");
    }

    /**
     * 从请求里获取formData参数
     *
     * @param request 请求
     * @return formData参数
     */
    public static Map<String, String> getFormData(HttpServletRequest request) {
        Map<String, String> formData = new HashMap<>(16);
        Map<String, String[]> parameterMap = request.getParameterMap();
        for (String key : parameterMap.keySet()) {
            String value = StrUtil.join(",", parameterMap.get(key));
            if (StrUtil.isNotEmpty(value)) {
                formData.put(key, value);
            }
        }
        return formData;
    }

    /**
     * formData转成字符串
     *
     * @param request 请求
     * @return 拼接后的字符串
     */
    public static String formDataToString(HttpServletRequest request) {
        return formDataToString(getFormData(request));
    }

    /**
     * formData转成字符串
     *
     * @param formData 参数
     * @return 拼接后的字符串
     */
    public static String formDataToString(Map<String, String> formData) {
        if (CollectionUtil.isEmpty(formData)) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        List<String> strings = new ArrayList<>(formData.keySet());
        Collections.sort(strings);
        for (String key : strings) {
            sb.append(key)
                    .append("=")
                    .append(formData.get(key))
                    .append("&");
        }
        return sb.toString().substring(0, sb.length() - 1);
    }

    /**
     * 判断url是否包含在排除列表里
     *
     * @param servletRequest 请求
     * @param excludeUrls    排除的url
     * @return 返回结果，如果servletRequest uri包含再excludeUrls key里则返回true ，反之返回false
     */
    public static boolean isExcludeUrl(ServletRequest servletRequest, Map<String, String> excludeUrls) {
        if (servletRequest instanceof HttpServletRequest) {
            String requestURI = format(((HttpServletRequest) servletRequest).getRequestURI());
            return CollectionUtil.isNotEmpty(excludeUrls) &&
                    excludeUrls.containsKey(requestURI);
        }
        return false;
    }

    /**
     * 错误响应封装
     *
     * @param response 响应
     * @param r        响应内容
     */
    public static void errorResp(ServletResponse response, R<?> r) throws IOException {
        String result = JSONUtil.toJsonStr(r);
        response.setContentType("application/json;charset=UTF-8");
        response.getOutputStream().write(result.getBytes(StandardCharsets.UTF_8));
        response.getOutputStream().flush();
    }

    /**
     * 匹配通配符表达式与uri是否匹配
     *
     * @param urlPatterns 表达式
     * @param uri         uri路径
     * @return 返回是否匹配 true匹配 false不匹配
     */
    public static boolean matched(String uri, String... urlPatterns) {
        if (urlPatterns == null || uri == null) {
            return false;
        }
        uri = format(uri);
        for (String urlPattern : urlPatterns) {
            if (urlPattern != null && uri.matches(urlPattern)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 格式uri ，将 //xxx///xxx/ 或这个 xxx/xxx格式化为
     * /xxx/xxx
     *
     * @param uri 原uri
     * @return 格式化后的uri
     */
    private static String format(String uri) {
        if (StrUtil.isEmpty(uri)) {
            return uri;
        }
        String split = "/";
        uri = (split + uri).replaceAll("/+", split);
        if (uri.lastIndexOf(split) == uri.length() - 1) {
            uri = uri.substring(0, uri.length() - 1);
        }
        return uri;
    }
}
